package com.hisense.davidmoten.rtree.geometry.internal;


import com.hisense.davidmoten.rtree.geometry.*;
import com.hisense.davidmoten.rtree.internal.Line2D;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.LineString;

import static com.hisense.davidmoten.rtree.geometry.Geometries.line;

/**
 * 多弧段线的实现
 * @author zy_john
 */
public class PolylineFloat implements Line {

    private LineString line;
    private Coordinate[] coordinates;

    private PolylineFloat(LineString line){
        this.line = line;
        this.coordinates = line.getCoordinates();
    }
    public PolylineFloat(){}
    public LineString getLine() {
        return line;
    }

    @Override
    public double x1() {
        double minX = coordinates[0].x;
        for (int i = 1; i < coordinates.length; i++) {
            if(coordinates[i].x < minX) minX = coordinates[i].x;
        }

        return minX;
    }

    @Override
    public double y1() {
        double minY = coordinates[0].y;
        for (int i = 1; i < coordinates.length; i++) {
            if(coordinates[i].y < minY) minY = coordinates[i].y;
        }
        return minY;
    }

    @Override
    public double x2() {
        double maxX =  coordinates[0].x;
        for (int i = 1; i < coordinates.length; i++) {
            if(coordinates[i].x > maxX) maxX = coordinates[i].x;
        }
        return maxX;
    }

    @Override
    public double y2() {
        double maxY = coordinates[0].y;
        for (int i = 1; i < coordinates.length; i++) {
            if(coordinates[i].y > maxY) maxY = coordinates[i].y;
        }
        return maxY;
    }

    @Override
    public boolean intersects(Line b) {
        //同类比较
        if(b.getClass() == PolylineFloat.class){
            return this.line.intersects(((PolylineFloat)b).getLine());
        }
        if(b.getClass() == LineFloat.class || b.getClass() == LineDouble.class){
            LineString lineString = GeometryUtil.getGeometryFactory()
                    .createLineString(new Coordinate[]{new Coordinate(b.x1(),b.y1()), new Coordinate(b.x2(),b.y2())});
            return this.line.intersects(lineString);
        }
        return false;
    }

    @Override
    public boolean intersects(Point point) {
        return this.line.intersects(GeometryUtil.getGeometryFactory()
                .createPoint(new Coordinate(point.x(), point.y())));
    }

    @Override
    public boolean intersects(Circle circle) {
        for (int i = 0; i < coordinates.length-1; i++) {
            Line line = line(coordinates[i].x, coordinates[i].y, coordinates[i+1].x, coordinates[i+1].y);
            if(line.intersects(circle)){
                return true;
            }
        }
        return false;
    }

    /**
     * <p>
     * Returns the distance to the given {@link Rectangle}. For a {@link Rectangle}
     * this might be Euclidean distance but for an EPSG4326 lat-long Rectangle might
     * be great-circle distance. The distance function should satisfy the following
     * properties:
     * </p>
     * <p>
     * <p>
     * <code>distance(r) &gt;= 0</code>
     * </p>
     * <p>
     * <p>
     * <code>if r1 contains r2 then distance(r1)&lt;=distance(r2)</code>
     * </p>
     *
     * @param r rectangle to measure distance to
     * @return distance to the rectangle r from the geometry
     */
    @Override
    public double distance(Rectangle r) {
        if (r.contains(this.x1(), this.y1()) || r.contains(this.x2(), this.y2())) {
            return 0;
        } else {
            double d1;
            d1 = distance(r.x1(), r.y1(), r.x1(), r.y2());
            if (d1 == 0)
                return 0;
            double d2 = distance(r.x1(), r.y2(), r.x2(), r.y2());
            if (d2 == 0)
                return 0;
            double d3;
            d3 = distance(r.x2(), r.y2(), r.x2(), r.y1());
            double d4;
            d4 = distance(r.x2(), r.y1(), r.x1(), r.y1());
            return Math.min(d1, Math.min(d2, Math.min(d3, d4)));
        }
    }
    private double distance(double x1, double y1, double x2, double y2) {
        Line2D line = new Line2D(x1, y1, x2, y2);
        double d1 = line.ptSegDist(this.x1(), this.y1());
        double d2 = line.ptSegDist(this.x2(), this.y2());
        Line2D line2;
        line2 = new Line2D(this.x1(), this.y1(), this.x2(), this.y2());
        double d3 = line2.ptSegDist(x1, y1);
        if (d3 == 0)
            return 0;
        double d4 = line2.ptSegDist(x2, y2);
        if (d4 == 0)
            return 0;
        else
            return Math.min(d1, Math.min(d2, Math.min(d3, d4)));

    }

    /**
     * Returns the minimum bounding rectangle of this geometry.
     *
     * @return minimum bounding rectangle
     */
    @Override
    public Rectangle mbr() {
        return Geometries.rectangle(x1(), y1(), x2(), y2());
    }

    @Override
    public boolean intersects(Rectangle r) {
       return this.line.intersects(GeometryUtil.getGeometryFactory()
                .createPolygon(new Coordinate[]{new Coordinate(r.x1(), r.y1()),
                new Coordinate(r.x1(), r.y2()), new Coordinate(r.x2(), r.y2()),
                new Coordinate(r.x2(), r.y1()),new Coordinate(r.x1(), r.y1())})
       );
    }

    @Override
    public boolean isDoublePrecision() {
        return false;
    }

    public static Line create(LineString line) {
        return new PolylineFloat(line);
    }
}
